package com.xbk.seckill.ratelimiter.qps.interceptor;

import cn.hutool.core.util.StrUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.xbk.seckill.common.exception.ErrorCode;
import com.xbk.seckill.common.exception.SeckillException;
import com.xbk.seckill.ratelimiter.qps.annotation.SeckillRateLimiter;
import com.xbk.seckill.ratelimiter.qps.bean.BHRateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 限流切面
 *
 * @author xiexu
 * @date 2025/5/5 21:52
 */
@Slf4j
@Aspect
@Component
@ConditionalOnProperty(prefix = "rate.limit.local.qps", name = "enabled", havingValue = "true")
public class RateLimiterInterceptor implements EnvironmentAware {

    /**
     * 本地缓存，用于存放限流器实例，key 为限流名称，value 为对应的限流器对象
     */
    private static final Map<String, BHRateLimiter> BH_RATE_LIMITER_MAP = new ConcurrentHashMap<>();

    /**
     * Spring 环境对象，用于获取配置属性等
     */
    private Environment environment;

    /**
     * 默认每秒许可数量，若注解中未指定则使用该值
     */
    @Value("${rate.limit.local.qps.default.permitsPerSecond:1000}")
    private double defaultPermitsPerSecond;

    /**
     * 默认获取许可的超时时间，单位由注解指定
     */
    @Value("${rate.limit.local.qps.default.timeout:1}")
    private long defaultTimeout;

    @Pointcut("@annotation(seckillRateLimiter)")
    public void pointCut(SeckillRateLimiter seckillRateLimiter) {

    }

    @Around(value = "pointCut(seckillRateLimiter)")
    public Object around(ProceedingJoinPoint pjp, SeckillRateLimiter seckillRateLimiter) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取当前类的简单类名
        String className = pjp.getTarget().getClass().getSimpleName();
        // 获取当前调用的方法名
        String methodName = signature.getName();
        // 从注解中获取配置的限流名称，并替换环境变量占位符
        String rateLimitName = environment.resolvePlaceholders(seckillRateLimiter.name());
        // 如果注解中未配置或配置非法，则使用类名 + 方法名作为默认限流名称
        if (StrUtil.isEmpty(rateLimitName) || rateLimitName.contains("${")) {
            rateLimitName = className + "-" + methodName;
        }
        // 获取对应的限流器对象
        BHRateLimiter rateLimiter = this.getRateLimiter(rateLimitName, seckillRateLimiter);
        // 尝试获取令牌
        boolean success = rateLimiter.tryAcquire();
        // 获取方法参数
        Object[] args = pjp.getArgs();
        // 如果获取成功，则执行原方法
        if (success) {
            return pjp.proceed(args);
        }
        // 获取失败，记录日志并抛出异常，提示用户稍后再试
        log.error("around|访问接口过于频繁|{},{}", className, methodName);
        throw new SeckillException(ErrorCode.RETRY_LATER);
    }

    /**
     * 获取或创建指定名称的限流器对象（带有双重检查锁的懒加载）
     */
    private BHRateLimiter getRateLimiter(String rateLimitName, SeckillRateLimiter seckillRateLimiter) {
        // 首先尝试从缓存中获取限流器对象
        BHRateLimiter bhRateLimiter = BH_RATE_LIMITER_MAP.get(rateLimitName);
        // 如果缓存中没有，则尝试创建一个新的限流器对象（加锁保证并发安全）
        if (bhRateLimiter == null) {
            // intern() 确保同步锁对象是同一个字符串常量池对象
            final String finalRateLimitName = rateLimitName.intern();
            synchronized (finalRateLimitName) {
                // 再次检查缓存，防止并发时重复创建
                bhRateLimiter = BH_RATE_LIMITER_MAP.get(rateLimitName);
                // 若仍未找到，则进行初始化
                if (bhRateLimiter == null) {
                    // 获取注解配置的 permitsPerSecond，若未设置则使用默认值
                    double permitsPerSecond = seckillRateLimiter.permitsPerSecond() <= 0 ? defaultPermitsPerSecond : seckillRateLimiter.permitsPerSecond();
                    // 获取注解配置的 timeout，若未设置则使用默认值
                    long timeout = seckillRateLimiter.timeout() <= 0 ? defaultTimeout : seckillRateLimiter.timeout();
                    // 获取时间单位
                    TimeUnit timeUnit = seckillRateLimiter.timeUnit();
                    // 创建一个新的限流器对象
                    bhRateLimiter = new BHRateLimiter(RateLimiter.create(permitsPerSecond), timeout, timeUnit);
                    // 将新的限流器加入缓存
                    BH_RATE_LIMITER_MAP.putIfAbsent(rateLimitName, bhRateLimiter);
                }
            }
        }
        return bhRateLimiter;
    }

    /**
     * Spring 回调注入 Environment 对象，用于解析配置项占位符
     */
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}
